/** ***********************************************************************************
* @file         net_socket_api.c
* @brief        网络数据处理API接口
* @details      主要用来主力网络链接、发送、接收关闭多链接异步通信机制
* @author       杨小珛
* @date         2020/12/23
* @copyright    北京高知科技有限公司    
* @version      V1.0
* @attention    无
**************************************************************************************/

#include "net_socket_api.h"
#include "log_task.h"
/*-------------------------------------------------------------------------
        全局变量定义(Global variable definition)
-----------------------------------------------------------------------*/
static uint32_t  socket_recv_data_put( uint32_t ConnIndex,
                                    uint8_t *pData,
                                    uint32_t InBufLen);
static void  socket_net_state_put( void * pv );
static uint32_t  socket_resource_free( uint32_t ConnIndex);

/** ****************************************************************************
* @remarks       uint32_t connect_type_check(uint8_t *pAddr)
* @brief         服务器地址类型判断
* @param[in]     *pAddr 服务器地址
* @param[out]    无
* @return        CONNECT_TYPE_DOMAIN CONNECT_TYPE_IP
* @attention     无
*******************************************************************************/
uint32_t connect_type_check(uint8_t *pAddr)
{
    uint32_t i=0;

    while(pAddr[i] != 0)
    {
        if((pAddr[i]<'z' && pAddr[i]>'a') || (pAddr[i]<'Z' && pAddr[i]>'A'))
        {
            return CONNECT_TYPE_DOMAIN;
        }
        i++;
    }

    return CONNECT_TYPE_IP;
}

/** ****************************************************************************
* @remarks       void socket_default( socket_handle_s *pxSocketHandle )
* @brief         初始化socket 连接参数
* @param[in]     *pxSocketHandle 服务器链接参数
* @param[out]    无
* @return        无
* @attention     无
*******************************************************************************/
void socket_default( socket_handle_s *pxSocketHandle )
{
    if( pxSocketHandle == NULL )
        return ;
    pxSocketHandle->connIndex           = 0xFF;
    pxSocketHandle->scoketId            = 0;
    pxSocketHandle->connStaus           = 0;
    pxSocketHandle->fDiscCallback       = NULL;
	memset((uint8_t *)pxSocketHandle->ip , 0x00 , MAX_IP_LEN);
    pxSocketHandle->port                = 0;
    pxSocketHandle->protocolType        = e_socket_protocol_init;
    pxSocketHandle->recvTotalBytes      = 0;
    pxSocketHandle->sendTotalBytes      = 0;
}

/** ****************************************************************************
* @remarks       uint32_t  socket_bind( socket_handle_s *pxSocketHandle, char *Ip, uint16_t Port, protocol_type_e ePtclType, socket_state_fun_t FDiscCallback )
* @brief         初始化socket 连接参数
* @param[in]     char *Ip                        : IP/域名地址
* @param[in]     Port                   : 端口号
* @param[in]     ePtclType       : 连接类型  e_socket_protocol_tcp, e_socket_protocol_udp e_socket_protocol_mqtt
*		        socket_state_fun_t FDiscCallback  : 链接断开时的回调函数
* @param[in]     FDiscCallback  : 链接断开时的回调函数
* @param[out]    *pxSocketHandle : SOCKET句柄
* @return        pdFAIL :失败 pdPASS 成功
* @attention     可重入，已连接的SOCKET会先断开
*******************************************************************************/
uint32_t  socket_bind( socket_handle_s *pxSocketHandle, char *Ip, uint16_t Port, protocol_type_e ePtclType, socket_state_fun_t FDiscCallback )
{
	if( strnlen( Ip, MAX_IP_LEN-1 ) >= MAX_IP_LEN-1 )
		return pdFAIL;
	
	/*如果已经连接则先断开*/
	if(( pxSocketHandle->connIndex != 0xFF ) && ( pxSocketHandle->connStaus == 1 ))
	{
		//gprs_debug_print(INFOR_MODE,"Socket Bind Closes Socket!\r\n",0);
		socket_close( (socket_handle_s *)pxSocketHandle );
	}
	socket_default( pxSocketHandle );
	strcpy((char*)pxSocketHandle->ip, (char*)Ip );
	pxSocketHandle->port                = Port;
	pxSocketHandle->fDiscCallback       = FDiscCallback ;
	pxSocketHandle->protocolType        = ePtclType;
	return pdPASS;
}
/** ****************************************************************************
* @remarks       uint32_t  socket_connect( socket_handle_s *pxSocketHandle )
* @brief         SOCKET连接
* @param[in]     *pxSocketHandle : SOCKET句柄
* @param[out]    无
* @return        CONNECT_NOINIT CONNECT_OK CONNECT_ERR CONNECT_CONFIG_ERR CONNECT_TIMEOUT
* @attention     半可重入，内部互斥，有阻塞
*******************************************************************************/
uint32_t  socket_connect( socket_handle_s *pxSocketHandle )
{
    uint32_t ret = pdFAIL;
	
    if( pxSocketHandle == NULL )
    {
        return pdFAIL;
    }
	if( pxSocketHandle->port == 0x00 )
	{
	  	return pdFAIL;
	}
	/*互斥体上锁*/
    gaoz_mutex_lock(net_link_mutex);
    ret = net_connect( (char*)pxSocketHandle->ip,
                          pxSocketHandle->port,
                          pxSocketHandle->protocolType,
                          (uint8_t*)&pxSocketHandle->connIndex,
                          (uint8_t*)&pxSocketHandle->connStaus,
                          (uint32_t*)&pxSocketHandle->scoketId,
                          connect_type_check((uint8_t*)pxSocketHandle->ip));
	
    if(( ret == pdPASS ) && ( pxSocketHandle->connStaus == 1 ))	/*创建网络连接成功*/
    {
    	net_socket_state_callback_set((socket_handle_s * )pxSocketHandle);
        net_recv_data_callback_set( socket_recv_data_put );
        socket_net_state_callback_set( socket_net_state_put );
    }
	/*互斥体解锁*/
    gaoz_mutex_unlock(net_link_mutex);
    if( ret == pdPASS )
    {
        if( socket_bmt.socket_recv_table[pxSocketHandle->connIndex] == 0 )
        {
            socket_bmt.socket_recv_table[pxSocketHandle->connIndex] = gaoz_queue_create( NET_REV_DATA_QUEUE_DEPTH, sizeof( net_rev_data_s ) );
        }
    }
    return ret;
    
}

/** ****************************************************************************
* @remarks       uint32_t  socket_close( socket_handle_s *pxSocketHandle )
* @brief         SOCKET关闭
* @param[in]     *pxSocketHandle : SOCKET句柄
* @param[out]    无
* @return        pdFAIL :失败 pdPASS 成功
* @attention     可重入
*******************************************************************************/
uint32_t  socket_close( socket_handle_s *pxSocketHandle )
{
    uint32_t ret = 0 , scoket_id = 0 , con_st = 0;
	protocol_type_e net_type = e_socket_protocol_init;
    if( pxSocketHandle == NULL )
    {
        return pdFAIL;
    }
    if( pxSocketHandle->connIndex >= MAX_SOCKET_CONN_NUM )
    {
        return pdFAIL;
    }
    con_st = pxSocketHandle->connStaus;
    scoket_id = pxSocketHandle->scoketId;
    net_type = pxSocketHandle->protocolType;
    
	socket_resource_free( pxSocketHandle->connIndex ); /*清空底层 网络数据*/
	socket_default( (socket_handle_s *)pxSocketHandle )/*清空自身数据*/;
    
    if( con_st != 0 )                                    /*当连接状态不为0时 可以进行网络关闭*/
    {
        ret = net_close( scoket_id , net_type );         /*网络连接异常已经被强制断开，不需要再次关闭SOCKET 节点了*/
    }
    return ret;
}
/** ****************************************************************************
* @remarks       static uint32_t  socket_resource_free( uint32_t ConnIndex)
* @brief         针对bmi结构，释放指定一条SOCKET所占用的资源
* @param[in]     ConnIndex : 索引号
* @param[out]    无
* @return        pdFAIL :失败 pdPASS 成功
* @attention     可重入
*******************************************************************************/
static uint32_t  socket_resource_free( uint32_t ConnIndex)
{
	uint32_t i = 0;
    net_rev_data_s rev_data;
	if( ConnIndex > MAX_SOCKET_CONN_NUM )
	{
		return pdFAIL;
	}
	// 清除、释放SOCKET句柄、消息接收队列
	for( i = 0; i < NET_REV_DATA_QUEUE_DEPTH; i++ )
	{	
		if( gaoz_queue_get(socket_bmt.socket_recv_table[ConnIndex], &rev_data, 1) == 0 )
		{
			gaoz_free_mem((void *)rev_data.pData);
		}
		else
		{
			break;
		}
	}
	// 句柄,清顶层<-->中间层句柄
	if( socket_bmt.socket_create_num > 0 )
    {
        socket_bmt.socket_create_num --;
    }
	socket_default( (socket_handle_s *)&socket_bmt.socket_handle_table[ConnIndex] );
	return pdPASS;
}
/** ****************************************************************************
* @remarks       uint32_t  socket_write( socket_handle_s *pxSocketHandle, uint8_t *pData, uint32_t Dlen )
* @brief         SOCKET数据发送
* @param[in]     *pxSocketHandle : SOCKET句柄
* @param[in]     *pData : 数据地址
* @param[in]     Dlen : 数据长度
* @param[out]    无
* @return        pdFAIL :失败 pdPASS 成功
* @attention     可重入，异步模式，无阻塞，队列满时10ms延时等待，返回成功只表示底层
*                接收成功，不意味对方IP已经收到，失败时表示塞队列超时
*******************************************************************************/
uint32_t  socket_write( socket_handle_s *pxSocketHandle, uint8_t *pData, uint32_t Dlen )
{	
#if 1
    if( pxSocketHandle == NULL )
    {
        return pdFAIL;
    }
    if( pxSocketHandle->connStaus != 1 ) /*网络连接异常时则不允许进行数据发送*/
    {
        return pdFAIL;
    }
    if( pxSocketHandle->connIndex >= MAX_SOCKET_CONN_NUM )
    {
        return pdFAIL;
    }
    if( (pData == NULL) || (Dlen == 0) ) 
    {
        return pdFAIL;
    }
#endif	
    if( net_send_data( pxSocketHandle->connIndex, (uint8_t*)pData, Dlen ) == pdPASS )
        return pdPASS;
    else
        return pdFAIL;
}

/** ****************************************************************************
* @remarks       uint32_t socket_recv(
*                         socket_handle_s  *pxSocketHandle,
*                         uint8_t *pData,
*                         uint32_t DelayMs )
* @brief         SOCKET数据接收
* @param[in]     *pxSocketHandle : SOCKET句柄
* @param[in]     *pData : 数据地址
* @param[in]     DelayMs : 延时时间
* @param[out]    无
* @return        接收数据的长度
* @attention     可重入,支持异步建立连接、收取数据
*******************************************************************************/
uint32_t  socket_recv(
                        socket_handle_s  *pxSocketHandle,
                        uint8_t *pData,
                        uint32_t DelayMs )
{
    uint32_t ret = 0;
    net_rev_data_s gprs_recv_data;
    do
	{
        if( ( pxSocketHandle->connStaus != 1 ) || ( socket_bmt.socket_recv_table[pxSocketHandle->connIndex] == 0 ) )
        {
            gaoz_task_sleep(10);
            if( DelayMs >= 10 )
                DelayMs -= 10;
            else
                DelayMs = 0;
            continue;
        }
        else
        {
            if( pxSocketHandle->connIndex > MAX_SOCKET_CONN_NUM )
            {
                break;
            }
            if( gaoz_queue_get(socket_bmt.socket_recv_table[pxSocketHandle->connIndex], &gprs_recv_data, DelayMs) == 0 )
            {
                if(  gprs_recv_data.pData != NULL )
                {
                    if( gprs_recv_data.DateLen > NET_SEND_DATA_LEN )
                    {
                        gprs_recv_data.DateLen = NET_SEND_DATA_LEN;
					}
                    if( pxSocketHandle->connIndex != gprs_recv_data.MsgIndex )
                    {
                        ret = 0;
                    }
                    else
                    {
                        ret = gprs_recv_data.DateLen;
                        memcpy( ( uint8_t * )pData, ( uint8_t * )gprs_recv_data.pData, gprs_recv_data.DateLen);
                    }
                    gaoz_free_mem(( void * )gprs_recv_data.pData);
					break;
                }
            }
			else
			{
				break;
			}
        }
    }while( DelayMs );
    return ret;
}

/** ****************************************************************************
* @remarks       uint32_t  socket_recv_data_put( uint32_t ConnIndex,
*                                                uint8_t *pData,
*                                                uint32_t InBufLen)
* @brief         SOCKET数据分发
* @param[in]     ConnIndex : SOCKET 通道索引号
* @param[in]     *pData  : 数据地址
* @param[in]     InBufLen : 数据长度
* @param[out]    无
* @return        pdFAIL :失败 pdPASS 成功
* @attention     可重入,供底层调用
*******************************************************************************/
static uint32_t  socket_recv_data_put( uint32_t ConnIndex,
                                    uint8_t *pData,
                                    uint32_t InBufLen)
{
    volatile uint32_t ret = pdFAIL;
    
    net_rev_data_s gprs_recv_data;
	
    if( ConnIndex > MAX_SOCKET_CONN_NUM )
        return 0;

    gprs_recv_data.MsgIndex = ConnIndex;
    
    if( InBufLen > NET_SEND_DATA_LEN )
        InBufLen = NET_SEND_DATA_LEN;
    
    gprs_recv_data.DateLen = InBufLen;
    gprs_recv_data.MsgValidUntil = 0;
    gprs_recv_data.pData = gaoz_malloc_mem( gprs_recv_data.DateLen + 8);
	//space_malloc_printf("\r\n<<<<<<<<<<<Socket recv malloc space %8X >>>>>>>>>>>>\r\n" , gprs_recv_data.pData );
    memcpy( (void*)gprs_recv_data.pData, (void*)pData, gprs_recv_data.DateLen);
    if( socket_bmt.socket_recv_table[ConnIndex] != 0 )
    {
        if( gaoz_queue_put(socket_bmt.socket_recv_table[ConnIndex], &gprs_recv_data, 10) == 0 )
        {
            ret = pdPASS;
        }
    }
    if( ret == pdFAIL )
    {
        gaoz_free_mem((void * )gprs_recv_data.pData);
    }
    return ret;
}
/** ****************************************************************************
* @remarks       static void  socket_net_state_put( void * pv )
* @brief         SOCKET 网络连接状态 输出
* @param[in]     * pv 不明格式入参
* @param[out]    无
* @return        无
* @attention     可重入,供底层调用
*******************************************************************************/
static void  socket_net_state_put( void * pv )
{
    socket_net_state_s * state = NULL ;

    if( pv == NULL )
    {
        return;
    }
    state = ( socket_net_state_s * )pv;
    if( state->index_id >= MAX_SOCKET_CONN_NUM )
    {
        return;
    }
    if( socket_bmt.socket_handle_table[state->index_id].fDiscCallback != NULL )
    {	
        socket_bmt.socket_handle_table[state->index_id].fDiscCallback(( void * )state );
    }
}

/** ****************************************************************************
* @remarks       uint32_t  socket_free_connect( char *ip, uint16_t port ,uint8_t type )
* @brief         SOCKET快速连接
* @param[in]     *ip : 服务器IP地址
* @param[in]     port : 服务器端口号
* @param[in]     type : 连接方式
* @param[out]    *socketid 连接的socket id
* @return        CONNECT_NOINIT CONNECT_OK CONNECT_ERR CONNECT_CONFIG_ERR CONNECT_TIMEOUT
* @attention     半可重入，内部互斥，有阻塞
*******************************************************************************/
uint32_t  socket_free_connect( char *ip, uint16_t port ,uint8_t type , uint32_t *socketid )
{   
    return net_free_connect( ip, port, type,    socketid , connect_type_check((uint8_t*)ip));
}

/** ****************************************************************************
* @remarks       uint32_t  socket_free_write( uint16_t sock_id ,uint8_t * data , uint16_t len )
* @brief         sock_id  
* @param[in]     *pxSocketHandle : SOCKET句柄
* @param[in]     *pData : 数据地址
* @param[in]     Dlen : 数据长度
* @param[out]    无
* @return        pdFAIL :失败 pdPASS 成功
* @attention     
*******************************************************************************/
uint32_t  socket_free_write( uint16_t sock_id ,uint8_t * data , uint16_t len )
{
    return net_free_send_data( sock_id ,data , len );
}

/** ****************************************************************************
* @remarks       int32_t  socket_free_recv( uint16_t sock_id ,uint8_t * data , uint16_t len )
* @brief         接收 TCP 数据函数，一次读取最长不超过 2048 字节。
* @param[in]     sock 创建的sock id  
* @param[in]     len 接收数据 data 首地址 
* @param[out]    *data 数据首地址
* @return        ≥0 - 实际接收到的数据长度(对端正常断开为 0) ＜0 - socket 错误（网络异常断开-1）
* @attention     无
*******************************************************************************/
int32_t  socket_free_recv( uint16_t sock_id ,uint8_t * data , uint16_t len )
{
    return net_free_recv( sock_id, data, len );
}

/** ****************************************************************************
* @remarks       uint32_t net_close(uint16_t index ,uint8_t type)
* @brief         关闭NET 网络链接
* @param[in]     sock_id 网络节点
* @param[in]     type 网络连接模式
* @param[out]    无
* @return        pdFAIL 发送失败 pdPASS 发送成功
* @attention     无
*******************************************************************************/
uint32_t  socket_free_close( uint16_t sock_id , uint8_t type )
{
    return net_close( sock_id , type );
}

